use std::{env, fs};

use pest::{Parser as _, error::LineColLocation};

#[derive(pest_derive::Parser)]
#[grammar = "lexer.pest"]
pub struct ExpressionParser;

pub fn print_tokens(pair: pest::iterators::Pair<Rule>) {
    for p in pair.into_inner() {
        if p.clone().into_inner().next().is_none() {
            // 叶子节点
            let rule = format!("{:?}", p.as_rule());
            if rule == "EOI" {
                break;
            }

            let content = if rule == "INTEGER_CONST" {
                // eprintln!("{} {} {:?}", rule, p.as_str(), p.as_str().as_bytes());

                let str_val = p.as_str();
                let parsed_val = match str_val {
                    s if s.starts_with("0x") || s.starts_with("0X") => {
                        i32::from_str_radix(&s[2..], 16).unwrap()
                    }
                    s if s.starts_with('0') && s.len() > 1 => {
                        i32::from_str_radix(&s[1..], 8).unwrap()
                    }
                    s => s.parse::<i32>().unwrap(),
                };
                format!("{}", parsed_val)
            } else {
                p.as_str().to_string()
            };

            let line = p.as_span().start_pos().line_col().0;
            eprintln!("{rule} {content} at Line {line}.");
        } else {
            print_tokens(p);
        }
    }
}

pub fn tokenize(input: &str) {
    let pair = match ExpressionParser::parse(Rule::program, input) {
        Ok(mut pair) => pair.next().unwrap(),
        Err(e) => {
            match e.line_col {
                LineColLocation::Pos((line, col)) => {
                    eprintln!("Error type A at Line {}: {} {}", line, col, e.line());
                }
                LineColLocation::Span((line, _), _) => {
                    eprintln!("Error type A at Line {}: {}", line, e.line());
                }
            }
            return;
        }
    };

    print_tokens(pair);
}

fn main() {
    // 收集命令行参数
    let args: Vec<String> = env::args().collect();

    // 检查是否提供了文件名
    if args.len() < 2 {
        eprintln!("Usage: {} <filename>", args[0]);
        std::process::exit(1);
    }

    // 获取文件名
    let filename = &args[1];

    // 读取输入文件
    let input = fs::read_to_string(filename).expect("Failed to read file");

    // 词法分析
    tokenize(&input);
}
